/*
 * OddEvenSorter.cpp
 *
 *  Created on:  Jun 12, 2009
 *      Author:  Zach Riggle A37627537
 *      Project: CSE410 Project 2
 */
#include <sys/types.h>
#include <sys/wait.h>
#include <sstream>
#include <pthread.h>
#include <unistd.h>
#include "Log.h"
#include "OddEvenSorter.h"

OddEvenSorter::OddEvenSorter(SafeNumberList *s) {
    if (s == NULL)
        error("Gave NULL to constructor");
    snl = s;
}

OddEvenSorter::~OddEvenSorter() {
}

void OddEvenSorter::setMode(Mode m) {
    mode = m;
}

string OddEvenSorter::getListAsString() {
    stringstream ss;

    ss << "{ ";
    for (int i = 0; i < snl->size(); i++) {
        ss << snl->get(i)->get() << ", ";
    }
    ss << "}";

    return ss.str();
}

void OddEvenSorter::run() {
    snl->sorted.set(0);
    while (snl->sorted.get() == 0) {
        info(getListAsString());
        snl->sorted.set(1);

        switch (mode) {
        case MULTI_PROCESS:
            multiProcess();
            break;
        case MULTI_THREAD:
            multiThread();
            break;
        case SINGLE_PROCESS:
            singleProcess();
            break;
        }
    }
}

void OddEvenSorter::singleProcess() {
    oddSort(snl);
    evenSort(snl);
}

void OddEvenSorter::multiThread() {
    pthread_t odd;
    pthread_t even;
    pthread_create(&odd, NULL, oddSort, snl);
    pthread_create(&even, NULL, evenSort, snl);
    pthread_join(even, NULL);
    pthread_join(odd, NULL);
}

void OddEvenSorter::multiProcess() {
    pid_t odd;
    pid_t even;

    // Create the odd-sort process
    odd = fork();

    if (odd == 0) {

        //		pthread_t odd_pthread;
        //		pthread_create(&odd_pthread, NULL, oddSort, snl);
        //		pthread_join(odd_pthread, NULL);
        oddSort(snl);
        exit(0);
    }

    // Create the even-sort process
    even = fork();
    if (even == 0) {
        //		pthread_t even_pthread;
        //		pthread_create(&even_pthread, NULL, evenSort, snl);
        //		pthread_join(even_pthread, NULL);
        evenSort(snl);
        exit(0);
    }

    // Wait for the processes to exit
    waitpid(odd, NULL, 0);
    waitpid(even, NULL, 0);
}

void sort(SafeNumberList *list, int startingOffset) {
    for (int x = startingOffset; x < (list->size() - 1); x += 2) {
        list->lock(x, x + 1);

        int first = list->get(x)->get();
        int second = list->get(x + 1)->get();

        if (first > second) {
            list->get(x)->set(second);
            list->get(x + 1)->set(first);

            list->sorted.set(0);
        }

        list->unlock(x, x + 1);
    }
}

// Even-odd sort
void *evenSort(void* pSafeList) {
    SafeNumberList *list = (SafeNumberList*) pSafeList;

    sort(list, 0);
    return NULL;
}
void *oddSort(void* pSafeList) {
    SafeNumberList *list = (SafeNumberList*) pSafeList;

    sort(list, 1);
    return NULL;
}
